package tasks;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;

import tools.Format;
import tools.Parametres;
import core.Bdecoder;
import core.BeValue;
import core.InvalidBEncodingException;
import core.Peer;
import core.PeerConnectException;
import core.Torrent;

public class RequestTracker implements Callable<ArrayList<Peer>> {

	private ArrayList<Peer> listePeers;
	private String url;
	private String request;

	public RequestTracker(String url, Torrent torrent){
		this.listePeers = new ArrayList<Peer>();
		this.url = url;
		try {
			this.request = "?info_hash="+Format.encodeURL(Format.byteArray2Hex(torrent.getHashInfo()))+
			"&peer_id="+new String(Parametres.peerId)+"&port="+Parametres.tcpPort+"&uploaded=0&downloaded=0&left="+torrent.getLength()+
			"&numwant=50&compact=1&supportcrypto=0&event=started";
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public ArrayList<Peer> call() throws Exception {

		try {
			URL link = new URL(this.url+this.request);
			System.out.println(this.url+this.request);
			URLConnection con = link.openConnection();     

			int len = con.getContentLength();
			if (len > 0) {            
				InputStream input = con.getInputStream();	            
				byte[] buffer = new byte[len];
				input.read(buffer);           

				Bdecoder bdecoder = new Bdecoder(new ByteArrayInputStream(buffer));    				
				BeValue result = bdecoder.bdecode();
				Map map = result.getMap();
				System.out.println(result);
				input.close();

				Set keys = map.keySet();
				Collection values = map.values();
				Iterator<String> iterKey = keys.iterator();	
				Iterator<BeValue> iterValue = values.iterator();
				String key = "";
				BeValue value = null;

				while(iterKey.hasNext() && iterValue.hasNext()) {
					key = iterKey.next();
					value = iterValue.next();				
					this.handle(key, value);
				}

				return this.listePeers;
			} else {
				this.listePeers = null;
			}

		} catch(IOException e) {
			this.listePeers = null;
			System.out.println("RequestTracker|IOException: "+e);
		} catch(InvalidBEncodingException e) {
			this.listePeers = null;
			System.out.println("RequestTracker|InvalidBEncodingException: "+e);
		} catch(Exception e) {
			this.listePeers = null;
			System.out.println("RequestTracker|Exception: "+e);
		}

		return this.listePeers;
	}

	private void handle(String key, BeValue value) throws InvalidBEncodingException {
		if(key.equals("complete")) {
			int complete = value.getInt();
		} else if(key.equals("incomplete")) {
			int incomplete = value.getInt();
		} else if(key.equals("tracker id")) {
			String trackerId = value.getString();
		} else if(key.equals("interval")) {
			int interval = value.getInt();
		} else if(key.equals("peers")) {
			byte[] peersBuffer = value.getBytes();

			if(peersBuffer.length%6==0) {
				byte[] ipBuffer = new byte[4];
				byte[] portBuffer = new byte[2];

				String ipPeer = "";
				int portPeer = -1;

				for(int i=0; i<peersBuffer.length; i+=6) {	
					System.arraycopy(peersBuffer, i, ipBuffer, 0, 4);
					System.arraycopy(peersBuffer, i+4, portBuffer, 0, 2);

					ByteBuffer bb = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);								

					ipPeer = "";
					bb.put(0, ipBuffer[0]); 
					ipPeer+=bb.order(ByteOrder.LITTLE_ENDIAN).getInt(0)+".";

					bb.put(0, ipBuffer[1]);                
					ipPeer+=bb.order(ByteOrder.LITTLE_ENDIAN).getInt(0)+".";

					bb.put(0, ipBuffer[2]);                
					ipPeer+=bb.order(ByteOrder.LITTLE_ENDIAN).getInt(0)+".";

					bb.put(0, ipBuffer[3]);                
					ipPeer+=bb.order(ByteOrder.LITTLE_ENDIAN).getInt(0);				                

					portPeer = Format.byteArrayToInt(portBuffer);
					Peer peer;
					try {						
						peer = new Peer(ipPeer, portPeer);
						this.listePeers.add(peer);

					} catch (PeerConnectException e) {
					} 

				}
			}
		}		
	}

}
